from hashlib import sha256

import time
import json
import ecdsa

SECRET_KEY = 'SZTU'
CONNECTED_NODE_ADDRESS = "http://127.0.0.1:8000"


def create_accounts():
    accounts = []
    for i in range(10):
        seed = str(time.time())
        time.sleep(0.001)
        account = sha256(seed.encode()).hexdigest()
        accounts.append(account)
    return accounts


# Generate new Keys
privateKey = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
publicKey = privateKey.get_verifying_key()

message = "Testing message for validation."

# Generate Signature
signature = privateKey.sign(message.encode())


# To verify if the signature is valid
# print(Ecdsa.verify(message, signature, publicKey))

class Block:
    def __init__(self, index, transactions, timestamp, previous_hash, hash=0, nonce=0):
        self.index = index
        self.transactions = transactions
        self.timestamp = timestamp
        self.previous_hash = previous_hash
        self.nonce = nonce
        self.hash = hash

    def _calculate_hash(self):
        block_string = json.dumps(self.__dict__, sort_keys=True, indent=4)
        return sha256(block_string.encode()).hexdigest()


class Blockchain:
    # difficulty of our PoW algorithm
    difficulty = 2
    #max_transactions_per_block = 5

    def __init__(self):
        self.unconfirmed_transactions = []
        self.chain = []

    def create_genesis_block(self):
        '''
        A function to generate genesis block and appends it to
        the chain. The block has index 0, previous_hash as 0, and
        a valid hash.
        '''
        genesis_block = Block(0, [], 0, "0")
        genesis_block.hash = genesis_block._calculate_hash()
        self.chain.append(genesis_block)
        return genesis_block

    @property
    def last_block(self):  # TODO part 1
        # Returns the last block if the length is greater than zero
        return self.chain[-1] if self.chain else None

    def get_height(self):  # TODO part 2
        return len(self.chain)

    def get_block(self, index):  # TODO part 3
        return self.chain[index].__dict__ 
    #__dict__是一个内置属性，它用于存储对象的属性和值的字典。
    # 当我们对一个对象使用__dict__时，它会返回一个字典，该字典包含了对象的所有属性以及它们对应的值。

    def add_block(self, block, proof):  # TODO part 4
        """
        A function that adds the block to the chain after verification.
        Verification includes:
        * Checking if the proof is valid.
        * The previous_hash referred in the block and the hash of latest block
          in the chain match.
        """
        previous_hash = self.last_block.hash
        if previous_hash != block.previous_hash: #验证区块的previous_hash是否与上一个区块的hash值匹配
            return False
        if not self.is_valid_proof(block, proof):#验证工作量证明是否有效
            return False
        block.hash = proof                       #将工作量证明值作为区块的hash值
        self.chain.append(block)                 #将区块添加至区块链
        return True

    @staticmethod
    def proof_of_work(block):  # TODO part 5
        """
        Function that tries different values of nonce to get a hash
        that satisfies our difficulty criteria.
        """
        block.nonce = 0 #nonce是一个用于PoW算法的计数器，它从0开始。
        computed_hash = block._calculate_hash()
        while not computed_hash.startswith('0' * Blockchain.difficulty):
            block.nonce += 1
            computed_hash = block._calculate_hash()
        '''
        寻找满足条件的哈希值:
        while not computed_hash.startswith('0' * Blockchain.difficulty):
        这是一个循环，它会一直运行，直到找到的哈希值以特定数量的零开头。
        这个数量由 Blockchain.difficulty 定义，难度越大，需要的零越多，计算难度也越大。
        block.nonce += 1：在每次循环中，nonce的值增加1。
        computed_hash = block._calculate_hash()：每次改变nonce后，重新计算区块的哈希值。
        '''
        return computed_hash

    def add_new_transaction(self, transaction):  # TODO part 6
        #添加一个新交易到未确认交易列表。
        self.unconfirmed_transactions.append(transaction)

    # Send Transaction
    def send_transaction(self, sender, receiver, data):  # TODO part 7
        transaction = {"from": sender, "to": receiver, "data": data}
        transaction["timestamp"] = time.time()
        self.add_new_transaction(transaction)
        return self.unconfirmed_transactions


    # send transaction with private key
    def _send_transaction(self, sender, receiver, data, privateKey):  # TODO part 8
        #将加密的信息发送至接收方
        transaction = {"from": sender, "to": receiver, "data": data}
        signature = self.sign_transaction(privateKey, transaction)
        self.add_new_transaction({"from": sender, "to": receiver, "data": data, "signature": signature,"timestamp":time.time()})
        return self.unconfirmed_transactions

    @classmethod
    def is_valid_proof(cls, block, block_hash):  # TODO part 9
        """
        Check if block_hash is valid hash of block and satisfies
        the difficulty criteria.
        """
        return (block_hash.startswith('0' * Blockchain.difficulty) and
                block_hash == block._calculate_hash())
        #如果前difficulty个字符都是0，并且计算出的哈希值与区块的哈希值相同，则返回True。

    @classmethod
    def check_chain_validity(cls, chain):
        #验证整个区块链的有效性。
        result = True
        previous_hash = "0"

        for block in chain:
            block_hash = block.hash
            # remove the hash field to recompute the hash again
            # using `_calculate_hash` method.
            delattr(block, "hash")

            if not cls.is_valid_proof(block, block_hash) or \
                    previous_hash != block.previous_hash:
                result = False
                break

            block.hash, previous_hash = block_hash, block_hash

        return result
    '''
    这段代码定义了一个名为check_chain_validity的类方法，它用于验证整个区块链的有效性。
    这是确保区块链完整性和安全性的关键步骤。该方法检查每个区块的哈希值是否正确，以及每个区块是否正确引用了前一个区块的哈希值。
    下面是对这段代码的详细解释：
    1. 类方法定义:
    -check_chain_validity是一个类方法，使用 `@classmethod` 装饰器定义(即cls)。这意味着它可以直接通过类而不是类的实例调用。
    2. 初始化变量:
    - `result`：设置为 `True`，用于表示区块链是否有效。
    - `previous_hash`：设置为 "0"，这是创世区块的前一个哈希值。
    3. 遍历区块链中的每个区块:
    - 使用 `for` 循环遍历传入的 `chain` 参数中的每个区块。
    4. 临时移除区块的哈希属性:
    - `delattr(block, "hash")`：这行代码从区块对象中移除了 `hash` 属性。这是为了在验证过程中重新计算哈希值，而不使用存储在区块中的哈希值。
    5. 验证区块的有效性:
    - `cls.is_valid_proof(block, block_hash)`：调用类中的 `is_valid_proof` 方法来检查区块的哈希值是否有效，即是否满足难度要求。
    - `previous_hash != block.previous_hash`：检查当前区块的前一个区块的哈希值是否与前一个区块的实际哈希值匹配。
    6. **更新哈希值和前一个哈希值**:
    - 如果区块有效，则将 `block.hash` 和 `previous_hash` 更新为当前区块的哈希值。
    7. 返回验证结果:
    - 如果在任何区块中发现问题，`result` 将被设置为 `False`，并且循环将提前结束。
    - 方法最终返回 `result`，表示整个区块链是否有效。
    总的来说，这个方法通过遍历区块链中的每个区块并验证其哈希值和与前一个区块的链接，确保了整个区块链的有效性和一致性。
    这对于维护区块链网络的安全性和信任至关重要。

        '''# 通过相邻区块previous_hash的一致性来检测区块链的完整性
    def check_block_validity(self, chain):  # TODO part 10
        result = True  # 假设区块链是完整的，设置结果为True
        previous_hash = "0"  # 创世区块没有前一个区块，所以它的前一个哈希值设置为"0"
        
        for block in chain:  # 遍历区块链中的每个区块
            if (previous_hash != block["previous_hash"]):  # 如果当前区块的previous_hash不等于前一个区块的哈希值
                result = False  # 设置结果为False，表示区块链不完整
                break  # 结束循环，因为已经找到了一个不匹配的区块
            else:
                previous_hash = block["hash"]  # 如果匹配，更新previous_hash为当前区块的哈希值
        return result  # 返回检测结果，True表示区块链完整，False表示区块链不完整

            

    def mine(self):
        """
        This function serves as an interface to add the pending
        transactions to the blockchain by adding them to the block
        and figuring out Proof Of Work.
        挖矿函数。
        通过对工作量证明的验证来将未确认的交易添加至区块。
        """
        if not self.unconfirmed_transactions:
            return False

        last_block = self.last_block

        new_block = Block(index=last_block.index + 1,
                          transactions=self.unconfirmed_transactions,
                          timestamp=time.time(),
                          previous_hash=last_block.hash)

        proof = self.proof_of_work(new_block)
        self.add_block(new_block, proof)

        self.unconfirmed_transactions = []

        return True

    def _mine(self, publickey, signature, message):
        if not self.unconfirmed_transactions:
            return False

        if publickey.verify(signature, message.encode()):
            last_block = self.last_block

            new_block = Block(index=last_block.index + 1,
                              transactions=self.unconfirmed_transactions,
                              timestamp=time.time(),
                              previous_hash=last_block.hash)

            proof = self.proof_of_work(new_block)
            self.add_block(new_block, proof)

            self.unconfirmed_transactions = []

            return True
        else:
            return False

    def get_chain(self):
        chain_data = []
        for block in self.chain:
            chain_data.append(block.__dict__)

            # return json.dumps(chain_data,indent=4)
        return chain_data


def create_chain_from_dump(chain_dump):  # 从给定的JSON数据创建区块链
    generated_blockchain = Blockchain()
    generated_blockchain.create_genesis_block()
    for idx, block_data in enumerate(chain_dump):
        if idx == 0:
            continue  # skip genesis block
        block = Block(block_data["index"],
                      block_data["transactions"],
                      block_data["timestamp"],
                      block_data["previous_hash"],
                      block_data["nonce"])
        proof = block_data['hash']
        added = generated_blockchain.add_block(block, proof)
        if not added:
            raise Exception("The chain dump is tampered!!")
    return generated_blockchain
